package com.mingke.java.NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author mingke
 * @function TODO
 * @date 2021/11/8
 * @desc IO多路复用+单线程轮询，利用NIO实现，也就是非阻塞型IO模型
 */
public class NIOSocketServer {
    static Integer cidNum = 10;
    static int serverPort = 2021;

    public static void main(String[] args) throws IOException {
        //用于处理请求的线程池
        final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 1000, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000));


        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(serverPort),1000);

        Selector selector = Selector.open();
        System.out.println("Server Socket has been start with port: "+serverPort);
        ssc.configureBlocking(false); //必须设置成非阻塞
        ssc.register(selector, SelectionKey.OP_ACCEPT); //serverSocket只关心accept

        while(true) {

            int num = selector.select();
            if(num == 0) {
                continue;
            }
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectionKeys.iterator();

            while(it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();
                if(key.isAcceptable()) {
                    //原来是等待链接才会阻塞，而这里是已经要链接了，就算阻塞了也会马上往下走
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false); //设置成非阻塞才能监听
                    sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(512) );
                    System.out.println("new connection");
                }
                if(key.isReadable()) {
                    SocketChannel clientSocketChannel = (SocketChannel)key.channel();
                    //System.out.println("socket readable");
                    if(!clientSocketChannel.isConnected()) {
                        clientSocketChannel.finishConnect();
                        key.cancel();
                        clientSocketChannel.close();
                        System.out.println("socket closed2");
                        continue;
                    }
                    ByteBuffer buffer = (ByteBuffer)key.attachment();
                    int len = clientSocketChannel.read(buffer);
                    Socket socket = clientSocketChannel.socket();

                    if(len == -1) {
                        clientSocketChannel.finishConnect();
                        key.cancel();
                        clientSocketChannel.close();
                        System.out.println("socket closed1");
                    } else {
                        threadPool.execute(new NIOSocketHandler(clientSocketChannel, buffer));
                    }
                }
            }
        }
    }
}
